﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Diagnostics;
using System.Windows.Media.Imaging;

namespace Master
{
    public class Lane : Label
    {
        /*************
         * Variables *
         *************/
        public TrafficLight trafficLight = null;
        private InterSection interSection;
        private List<Direction> AllowedDirectionsList = new List<Direction>();

        /*************************
         * DEPENDENCY PROPERTIES *
         *************************/
        public static readonly DependencyProperty AllowedDirectionsProperty, MaxSpeedProperty, 
            OrientationProperty, TypeProperty;

        public double MaxSpeed
        {
            get { return (double)this.GetValue(MaxSpeedProperty); }
            set { this.SetValue(MaxSpeedProperty, value); }
        }

        public int Orientation
        {
            get { return (int)this.GetValue(OrientationProperty); }
            set { this.SetValue(OrientationProperty, value); }
        }

        public LaneType Type
        {
            get { return (LaneType)this.GetValue(TypeProperty); }
            set { this.SetValue(TypeProperty, value); }
        }

        /**
        * Allowed Directions
        * 0 = forward
        * 1 = left
        * 2 = right
        * 3 = forward, left
        * 4 = forward, right
        * 5 = left, right
        * 6 = forward, left, right
        * 7 = none
        */
        public int AllowedDirections
        { 
            get { return (int)this.GetValue(AllowedDirectionsProperty); }
            set { this.SetValue(AllowedDirectionsProperty, value); }
        }

        private static void AllowedDirectionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            List<Direction> directions = new List<Direction>();
            int value;
            if (int.TryParse(e.NewValue.ToString(), out value))
            {
                switch (value)
                {
                    case 0:
                        directions.Add(Direction.FORWARD);
                        break;
                    case 1:
                        directions.Add(Direction.LEFT);
                        break;
                    case 2:
                        directions.Add(Direction.RIGHT);
                        break;
                    case 3:
                        directions.Add(Direction.FORWARD);
                        directions.Add(Direction.LEFT);
                        break;
                    case 4:
                        directions.Add(Direction.FORWARD);
                        directions.Add(Direction.RIGHT);
                        break;
                    case 5:
                        directions.Add(Direction.LEFT);
                        directions.Add(Direction.RIGHT);
                        break;
                    case 6:
                        directions.Add(Direction.FORWARD);
                        directions.Add(Direction.LEFT);
                        directions.Add(Direction.RIGHT);
                        break;
                    case 7:
                        directions.Clear();
                        break;
                    default:
                        throw new InvalidOperationException("No allowed direction given!"); 
                    
                }
                Lane l = (Lane)d;
                l.AllowedDirectionsList = directions;
            }
            else
            {
                throw new System.ArgumentException("value type must be integer", "AllowedDirectionsProperty");
            }
        }

        /****************
         * CONSTRUCTORS *
         ****************/
        static Lane()
        {
            FrameworkPropertyMetadata AllowedDirectionsPropertyMetaData = new FrameworkPropertyMetadata(-1,
                    new PropertyChangedCallback(Lane.AllowedDirectionsPropertyChanged));

            FrameworkPropertyMetadata MaxspeedPropertyMetaData = new FrameworkPropertyMetadata((double)10);
            FrameworkPropertyMetadata OrientationPropertyMetaData = new FrameworkPropertyMetadata((int)0);
            FrameworkPropertyMetadata TypePropertyMetaData = new FrameworkPropertyMetadata((LaneType)LaneType.DEFAULT);

            AllowedDirectionsProperty = DependencyProperty.Register("AllowedDirections",
                    typeof(int), typeof(Lane), AllowedDirectionsPropertyMetaData);
            MaxSpeedProperty = DependencyProperty.Register("MaxSpeed",
                typeof(double), typeof(Road), MaxspeedPropertyMetaData);
            OrientationProperty = DependencyProperty.Register("Orientation",
                typeof(int), typeof(Lane), OrientationPropertyMetaData);
            TypeProperty = DependencyProperty.Register("Type",
                typeof(LaneType), typeof(Lane), TypePropertyMetaData);
        }

        public Lane()
        {
            this.Type = LaneType.DEFAULT;
            this.Initialized += new EventHandler(Lane_Initialized);
        }


        /******************
         * INITIALIZATION *
         ******************/
        void Lane_Initialized(object sender, EventArgs e)
        {
            BorderBrush = Brushes.White;

            switch (Orientation)
            {
                case 0:
                    Background = Brushes.Gray;
                    BorderThickness = new Thickness(2, 0, 0, 0);
                    break;
                case 90:
                    Background = Brushes.Gray;

                    BorderThickness = new Thickness(0, 2, 0, 0);
                    break;
                case 180:
                    this.Background = Brushes.Gray;

                    BorderThickness = new Thickness(0, 0, 2, 0);
                    break;
                case 270:
                    this.Background = Brushes.Gray;
                    BorderThickness = new Thickness(0, 0, 0, 2);
                    break;
            }

            if (AllowedDirections == -1)
            {
                throw new System.ArgumentException("Property must be set", "AllowedDirections");
            }
        }

        /********************
         * PUBLIC FUNCTIONS *
         ********************/

        public void setTrafficLight()
        {
            TrafficLight t = new TrafficLight();
            t.Height = 50;
            t.Width = 50;
            t.Orientation = Orientation;
            Point trafficLightLocation = getTrafficLightLocation(t);
            trafficLight = t;
            Canvas.SetTop(t, trafficLightLocation.Y);
            Canvas.SetLeft(t, trafficLightLocation.X);
            Canvas.SetZIndex(t, 3);
            Map.INSTANCE.Children.Add(t);
        }

        public Boolean hasTrafficLight()
        {
            return (trafficLight != null);
        }

        public Point getStartPoint()
        {
            Point point = new Point();
            Rect bounds = Toolkit.getBounds(this);
            if (this.Orientation == 0)
            {
                point.X = (bounds.Left + bounds.Right) / 2;
                point.Y = bounds.Bottom;
            }
            else if (this.Orientation == 90)
            {
                point.X = bounds.Left;
                point.Y = (bounds.Top + bounds.Bottom) / 2;
            }
            else if (this.Orientation == 180)
            {
                point.X = (bounds.Left + bounds.Right) / 2;
                point.Y = bounds.Top;
            }
            else if (this.Orientation == 270)
            {
                point.X = bounds.Right;
                point.Y = (bounds.Top + bounds.Bottom) / 2;
            }
            return point;
        }

        public Point getDestinationPoint()
        {
            Point point = new Point();
            Rect bounds = Toolkit.getBounds(this);
            if (this.Orientation == 0)
            {
                point.X = (bounds.Left + bounds.Right) / 2;
                point.Y = bounds.Top;
            }
            else if (this.Orientation == 90)
            {
                point.X = bounds.Right;
                point.Y = (bounds.Top + bounds.Bottom) / 2;
            }
            else if (this.Orientation == 180)
            {
                point.X = (bounds.Left + bounds.Right) / 2;
                point.Y = bounds.Bottom;
            }
            else if (this.Orientation == 270)
            {
                point.X = bounds.Left;
                point.Y = (bounds.Top + bounds.Bottom) / 2;
            }
            return point;
        }

        public void setTrafficArrow()
        {
            if (Type == LaneType.DRIVEWAY)
            {
                Image img = new Image();
                img.Height = 50;
                img.Width = 50;
                img.RenderTransform = new RotateTransform(Orientation);
                switch (AllowedDirections)
                {
                    case 0:
                        img.Source = new BitmapImage(new Uri(@"/Master;component/Images/arrowUp.png", UriKind.Relative));
                        break;
                    case 1:
                        img.Source = new BitmapImage(new Uri(@"/Master;component/Images/arrowLeft.png", UriKind.Relative));
                        break;
                    case 2:
                        img.Source = new BitmapImage(new Uri(@"/Master;component/Images/arrowRight.png", UriKind.Relative));
                        break;
                    case 3:
                        img.Source = new BitmapImage(new Uri(@"/Master;component/Images/arrowLeftForward.png", UriKind.Relative));
                        break;
                    case 4:
                        img.Source = new BitmapImage(new Uri(@"/Master;component/Images/arrowRightForward.png", UriKind.Relative));
                        break;
                    case 5:
                        img.Source = new BitmapImage(new Uri(@"/Master;component/Images/arrowLeftRight.png", UriKind.Relative));
                        break;
                }
                Point p = getTrafficArrowLocation(img);
                Canvas.SetTop(img, p.Y);
                Canvas.SetLeft(img, p.X);
                Canvas.SetZIndex(img, 1);
                Map.INSTANCE.Children.Add(img);
            }
        }

        public void setInterSection(InterSection interSection)
        {
            this.interSection = interSection;
        }

        public InterSection getInterSection()
        {
            return interSection;
        }

        public void setAllowedDirections(List<Direction> AllowedDirections)
        {
            this.AllowedDirectionsList = AllowedDirections;
        }

        public List<Direction> getAllowedDirections()
        {
            return AllowedDirectionsList;
        }

        /*********************
         * PRIVATE FUNCTIONS *
         *********************/
        private Point getTrafficLightLocation(TrafficLight t)
        {
            Rect bounds = Toolkit.getBounds(this);
            Point trafficLightLocation = new Point();
            switch (Orientation)
            {
                case 0:
                    trafficLightLocation.X = bounds.Left + ((bounds.Width - t.Width) / 2);
                    trafficLightLocation.Y = bounds.Top;
                    break;
                case 90:
                    trafficLightLocation.X = bounds.Left + (bounds.Width - t.Width);
                    trafficLightLocation.Y = bounds.Top + ((bounds.Height - t.Height) / 2);
                    break;
                case 180:
                    trafficLightLocation.X = bounds.Left + ((bounds.Width - t.Width) / 2);
                    trafficLightLocation.Y = bounds.Top + (bounds.Height - t.Height);
                    break;
                case 270:
                    trafficLightLocation.X = bounds.Left;
                    trafficLightLocation.Y = bounds.Top + ((bounds.Height - t.Height) / 2);
                    break;
                default: throw new InvalidOperationException("This lane has a unknown orientation");
            }
            return trafficLightLocation;
        }

        private Point getTrafficArrowLocation(Image img)
        {
            Rect bounds = Toolkit.getBounds(this);
            Point trafficArrowLocation = new Point();
            switch (Orientation)
            {
                case 0:
                    trafficArrowLocation.X = bounds.Left + ((bounds.Width - img.Width) / 2);
                    trafficArrowLocation.Y = bounds.Top + 50;
                    break;
                case 90:
                    trafficArrowLocation.X = bounds.Left + (bounds.Width - img.Width);
                    trafficArrowLocation.Y = bounds.Top + ((bounds.Height - img.Height) / 2);
                    break;
                case 180:
                    trafficArrowLocation.X = bounds.Left + ((bounds.Width - img.Width) / 2) + 45;
                    trafficArrowLocation.Y = bounds.Top + (bounds.Height - img.Height);
                    break;
                case 270:
                    trafficArrowLocation.X = bounds.Left + 50;
                    trafficArrowLocation.Y = bounds.Top + ((bounds.Height - img.Height) / 2) + 50;
                    break;
                default: throw new InvalidOperationException("This lane has a unknown orientation");
            }
            return trafficArrowLocation;
        }
    }
}
